﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Medianamik.Core;
using Medianamik.UI.Web.Shop.Data;
using Medianamik.UI.Web.Shop.Data.Dto;
using Medianamik.UI.Web.Shop.Data.Repositories;
using IRegion = Medianamik.UI.Web.Shop.Data.Interfaces.IRegion;
using ITax = Medianamik.UI.Web.Shop.Data.Interfaces.ITax;

namespace Medianamik.UI.Web.Shop.Services
{
    public interface IRegionAndTaxService
    {
        IList<TaxDTO> CalculateTaxes(double amount, IList<Guid> regions);
        TaxDTO CalculateTax(double amount, Guid taxId);
    }

    public class RegionAndTaxService<TTax, TRegion> : IRegionAndTaxService
        where TTax: ITax
        where TRegion: IRegion
    {
        private readonly IRegionRepository<TRegion> _regionRepository;
        private readonly ITaxRepository<TTax> _taxRepository;


        public RegionAndTaxService(IRegionRepository<TRegion> regionRepository,
            ITaxRepository<TTax> taxRepository)
        {
            _regionRepository = regionRepository;
            _taxRepository = taxRepository;
        }

        public virtual IList<TaxDTO> CalculateTaxes(double amount, IList<Guid> regions)
        {
            var taxesDto = new List<TaxDTO>();

            foreach (var regionAndTaxDto in
                regions.Select(region => TaxesAndRegionDTO.Where(tax => tax.Region.NodeId == region).ToList()))
            {
                taxesDto.AddRange(regionAndTaxDto.Select(item => CalculateTax(amount, item.Tax.NodeId)));
            }

            return taxesDto;
        }

        public virtual TaxDTO CalculateTax(double amount, Guid taxId)
        {
            var regionAndTaxDto = TaxesAndRegionDTO.Single(t => t.Tax.NodeId == taxId);
            var total = amount;
            double tax = 0;

            if (!regionAndTaxDto.Tax.IsRecursive)
            {
                tax += amount * regionAndTaxDto.Tax.TaxRate;
            }
            else
            {
                total = amount + CalculateTax(total, TaxesAndRegionDTO.Single(t => t.Region.NodeId == regionAndTaxDto.Region.ParentNodeId).Tax.NodeId).Tax;
                tax += total * regionAndTaxDto.Tax.TaxRate;
            }

            var resultTaxDto = CreateTaxDto(regionAndTaxDto, tax);

            return resultTaxDto;
        }

        protected virtual TaxDTO CreateTaxDto(RegionAndTaxDto regionAndTaxDto, double tax)
        {
            return new TaxDTO
               {
                   RegionName = regionAndTaxDto.Region.Name,
                   Tax = tax,
                   RoundedTax = Math.Round(tax, 2),
                   TaxName = regionAndTaxDto.Tax.Name,
                   TaxRate = regionAndTaxDto.Tax.TaxRate,
                   TaxId = regionAndTaxDto.Tax.NodeId
               };
        }

        private IList<RegionAndTaxDto> _taxesAndRegionsDTO;
        protected IList<RegionAndTaxDto> TaxesAndRegionDTO
        {

            get { return _taxesAndRegionsDTO ?? (_taxesAndRegionsDTO = GetTaxesAndRegionsDto()); }
        }

        protected virtual IList<RegionAndTaxDto> GetTaxesAndRegionsDto()
        {
            IEnumerable<TTax> taxes = _taxRepository.All(); 
            IEnumerable<TRegion> regions = _regionRepository.All();

            return taxes.Select(tax => new RegionAndTaxDto
                               {
                                   Tax = tax, Region = regions.Single(region => region.NodeId == tax.ParentNodeId)
                               }).ToList();
        }
    }

    public class RegionAndTaxService : RegionAndTaxService<Tax, Region>
    {
        public RegionAndTaxService(IRegionRepository<Region> regionRepository, 
            ITaxRepository<Tax> taxRepository) : base(regionRepository, taxRepository)
        {
        }
    }
}
